//
//$Id: qap.h 441 2010-04-09 17:27:20Z Oleg.Bulychov $
#pragma once

#include "src/heo/include/lf_endl.h"
#include "src/heo/include/random.h"
#include "src/heo/include/reflection.h"
#include "src/heo/include/solution.h"
#include "src/heo/include/stream_serializer.h"
#include "src/heo/include/optimization_direction.h"
#include "src/heo/include/array.h"
#include <vector>

struct QAP_Config
{
    int permutations_;

    static char const* get_Name() { return "qap"; }

    template<class V>
    void accept(V& v)
    {
        v(VAR_(permutations), 2);
    }
};
//__________________________________________________________________________________
struct QAP_Problem
{
    int n_;                             // dimension 
    std::vector< std::vector<int> > A_; // flow matrix
    std::vector< std::vector<int> > B_; // distance matrix

    explicit QAP_Problem()
    {}

    static char const* get_Default_File_Name() { return "a.dat"; }

    template<class I, class E>
    bool read(I& in, E* env, uniform_random& rnd)
    {
        in >> n_;

        A_.resize(n_);
        B_.resize(n_);
        for (int i = 0; i < n_; ++i)
        {
            A_[i].resize(n_);
            B_[i].resize(n_);
        }

        for (int i = 0; i < n_; ++i)
            for (int j = 0; j < n_; ++j)
                in >> A_[i][j];

        for (int i = 0; i < n_; ++i)
            for (int j = 0; j < n_; ++j)
                in >> B_[i][j];

        return true;
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________
class QAP_Solution
{
    //DISALLOW_COPY_AND_ASSIGN(QAP_Solution);
public:
    typedef int                     TCostType;
    typedef minimization<TCostType> TDirection;
protected:
    TCostType           cost_;
    bool                changed_;
    Array<int>          p_;
public:
    explicit QAP_Solution(): p_(), cost_(0), changed_(false)
    {}

    ~QAP_Solution()
    {
    }

    template<class E>
    void init(E* env, uniform_random& rnd)
    {//:NOTE: may be called several times!
        p_.set_Size(env->problem_.n_);
    }

    template<class E>
    void generate(E* env, uniform_random& rnd)
    {
        int n = int(p_.size());

        for (int i = 0; i < n; ++i)
            p_[i] = i;

        for (int k = 0; k < n; ++k)
        {
            int i = rnd.next_UInt(n);
            int j = rnd.next_UInt(n);
            std::swap(p_[i], p_[j]);
        }
        
        changed_ = true;
        //evaluate(env, rnd);    // debuging
        //print(std::cout, env); // debuging
    }

    TCostType get_Cost() const
    {
        ASSERT_(!changed_);
        return cost_;
    }

    template<class E>
    void evaluate(E* env, uniform_random& rnd)
    {
        if (!changed_)
            return;

        changed_ = false;

        cost_ = 0;
        int n = int(p_.size());
        for (int i = 0; i < n; ++i)
            for(int j = 0; j < n; ++j)
                cost_ += env->problem_.A_[i][j] * env->problem_.B_[p_[i]][p_[j]];
    }

    void assign(QAP_Solution const& sol)
    {
        changed_ = sol.changed_;
        cost_    = sol.cost_;
        p_       = sol.p_;
    }

    template<class X>
    void copy_To(X& x) const
    {
        x.assign(*this);
    }

	template<class E>
    void print(std::ostream& os, E* env)
    {
        os << lf
            << "cost: " << get_Cost() << lf;
 
        os << "assignment: ";
        for (int i = 0; i < (int) p_.size(); ++i)
            os << p_[i] << " ";

        os << lf;
    }

    template<class E>
    static bool read(E* env, std::string const& file_name, Solution_Pool<QAP_Solution>& pool)
    {
        std::ifstream in(file_name.c_str(), std::ios::binary);
        if (!in)
        {
            std::cout << "ERROR: can not open '" << file_name << "'" << std::endl;
            return false;
        }
        meta::ifstream_visitor bin_in(in);

        //:read count of solutions
        size_t count;
        bin_in >> count;
        char magic;
        in.read(&magic, 1);
        if ('*' != magic)
            return false;

        pool.resize(count);
        uniform_random rnd(0);
        for (Solution_Pool<QAP_Solution>::iterator it = pool.sol_list_.begin();
            it != pool.sol_list_.end(); ++it)
        {
            it->init(env, rnd);
            bin_in >> it->p_;
            it->changed_ = true;
            in.read(&magic, 1);
            if ('*' != magic)
                return false;
        }
        return true;
    }

    template<class E>
    void write(E* env, std::string const& file_name, size_t id, size_t count)
    {
        std::ofstream out(file_name.c_str(), (id ? std::ios::binary | std::ios::app : std::ios::binary));
        meta::ofstream_visitor bin_out(out);
        if (!id)
        {//:write count of solutions
            bin_out << count;
            out.write("*", 1);
        }
        bin_out << p_;
        out.write("*", 1);
    }

    template<class V>
    void accept(V& v)
    {
        v(cost_);
        v(changed_);
        v(p_);
    }
};
//__________________________________________________________________________________

//EOF!
